﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Diagnostics;

namespace Compiler
{
    public class Production
    {
        private List<Symbol> tail;

        public readonly int ID;
        public readonly Nonterminal Head;
        public readonly ReadOnlyCollection<Symbol> Tail;

        public Production(int ID, Nonterminal Head, params Symbol[] Symbols)
        {
            this.ID = ID;
            this.Head = Head;
            this.tail = new List<Symbol>(Symbols);
            this.Tail = new ReadOnlyCollection<Symbol>(tail);
        }

        public Production(int ID, Nonterminal Head, List<Symbol> Symbols)
            : this(ID, Head, Symbols.ToArray())
        {}

        public bool isEpsilonProduction()
        {
            return tail.Count == 1 && tail[0] == Symbol.Epsilon;
        }

        public IEnumerable<Nonterminal> GetNonTerminalsInTail()
        {
            return tail.OfType<Nonterminal>().Distinct();
        }

        public IEnumerable<Terminal> GetTerminalsInTail()
        {
            return tail.OfType<Terminal>().Distinct();
        }

        public override string ToString()
        {
            string tailstr = tail.Select(s => s.ToString()).Aggregate((a, b) => a + " " + b);
            return string.Format("{0} -> {1} \t({2})", Head.Name, tailstr, ID);
        }

        public class ProductionEqualityComparer : IEqualityComparer<Production>
        {
            Symbol.SymbolEqualityComparer symbolCmp = new Symbol.SymbolEqualityComparer();

            public bool Equals(Production x, Production y)
            {
                if (ReferenceEquals(x, y))
                    return true;

                if (!symbolCmp.Equals(x.Head, y.Head)
                    || x.tail.Count != y.tail.Count)
                    return false;

                for (int i = 0; i < x.tail.Count; ++i)
                    if (!symbolCmp.Equals(x.tail[i], y.tail[i]))
                        return false;

                return true;
            }

            public int GetHashCode(Production obj)
            {
                //kann verbessert werden durch einbeziehen des Tails.
                return symbolCmp.GetHashCode(obj.Head);
            }
        }
    }
}
